package com.open.admin.security.config;


import com.open.admin.security.filter.JwtAuthenticationTokenFilter;
import com.open.system.anno.IgnoreWebSecurity;
import com.open.system.handler.AccessDeniedHandlerImp;
import com.open.system.handler.AuthenticationEntryPointImp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {


    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 获取AuthenticationManager（认证管理器），登录时认证使用
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }


    public String[] getIgnoreApi() {

        Set<String> ignorePathSet = new HashSet<>();

        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            HandlerMethod handlerMethod = entry.getValue();
            if (handlerMethod.hasMethodAnnotation(IgnoreWebSecurity.class)) {
                ignorePathSet.addAll(entry.getKey().getPatternValues());
            }
        }

        return ignorePathSet.toArray(new String[0]);
    }


    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers(getIgnoreApi());
    }


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        HttpSecurity httpSecurity = http.authorizeHttpRequests(auth -> {
            //放行静态资源
            auth.requestMatchers("static/**","resource/**");
            //放行接口
            auth.requestMatchers(getIgnoreApi()).permitAll();
            //其他所有请求需要登录, anyRequest 不能配置在 antMatchers 前面
            auth.anyRequest().authenticated();
        });

         httpSecurity
                 //禁用csrf
                 .csrf(AbstractHttpConfigurer::disable)
                 //跨域配置
                 .cors(cors -> cors.configurationSource(corsConfigurationSource()));

        //处理器
        http.exceptionHandling(e -> {
                    e.accessDeniedHandler(new AccessDeniedHandlerImp());
                    e.authenticationEntryPoint(new AuthenticationEntryPointImp());
                }
        );
        //过滤器
        http.addFilterBefore(
                new JwtAuthenticationTokenFilter(),
                UsernamePasswordAuthenticationFilter.class
        );

        return http.build();
    }


    /**
     * 跨越配置
     */
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(List.of("*"));
        corsConfiguration.setAllowedMethods(List.of("*"));
        corsConfiguration.setAllowedOrigins(List.of("*"));
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
}

